using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public static class Compare
    {

        public static NodeVector EQ(params NodeVector[] pnvsIn)
        {
            NodeVector nvFirst = pnvsIn[0];
            return EQ(nvFirst.ParentComponent, pnvsIn);
        }

        public static NodeVector EQ(int piConstant,params NodeVector[] pnvsIn)
        {
            NodeVector nvFirst = pnvsIn[0];
            return EQ(nvFirst.ParentComponent,piConstant, pnvsIn);
        }

        public static NodeVector EQ(Component poComponent, int piConstant, params NodeVector[] pnvsIn)
        {
            return EQ(poComponent, CONST(poComponent, piConstant));
        }
        
        public static NodeVector EQ(Component poComponent, params NodeVector[] pnvsIn)
        {
            System.Collections.ArrayList alOut = new System.Collections.ArrayList();

            NodeVector nvFirst = pnvsIn[0];

            int iIdx = 0;
            foreach (NodeVector nvIn in pnvsIn)
            {
                iIdx++;
                if (iIdx > 0)
                {
                    string sName = poComponent.NewComponentName("compare_" + nvFirst.Name);
                    int iBWidth = nvIn.Length;
                    if (iBWidth <= nvFirst.Length)
                        iBWidth = nvFirst.Length;

                    CompareEqual oCompare = new CompareEqual(poComponent, sName, nvFirst.Length, iBWidth);
                    oCompare.InputNodesA.Connection = nvFirst;
                    oCompare.InputNodesB.Connection = nvIn;
                    alOut.Add(oCompare);
                }
            }

            if (alOut.Count > 1)
            {
                string sName = poComponent.NewNodeVectorName("compare_" + nvFirst.Name + "_all");
                NodeVector nvCompares = new NodeVector(poComponent, sName, alOut.Count);

                int iBit = 0;
                foreach (CompareEqual oCompare in alOut)
                {
                    iBit++;
                    oCompare.Output.ConnectSubset(nvCompares, iBit, iBit);
                }
                return AND(nvCompares);
            }
            
            string sOutName = poComponent.NewNodeVectorName("compare_" + nvFirst.Name + "_out");
            CompareEqual oCompareOut = (CompareEqual)alOut[0];
            NodeVector nvOut = new NodeVector(poComponent, sOutName, 1);
            nvOut.Connection = oCompareOut.Output;
            return nvOut;
        }

        public static NodeVector NOT(params NodeVector[] pnvsIn)
        {
            NodeVector nvFirst = pnvsIn[0];
            return NOT(nvFirst.ParentComponent, pnvsIn);
        }


        public static NodeVector NOT(Component poComponent, params NodeVector[] pnvsIn)
        {
            string sFirstName="out";
            int iBits = 0;
            foreach (NodeVector nvIn in pnvsIn)
            {
                iBits += nvIn.Length;
                sFirstName = nvIn.Name;
            }

            string sName = poComponent.NewComponentName("not");
            NotGate oNot = new NotGate(poComponent, sName, iBits);
            sName = poComponent.NewNodeVectorName("not_" + sFirstName);
            NodeVector nvOut = poComponent.CreateNodeVector(sName, iBits);
            oNot.OutputNodes.Connection = nvOut;

            int iBit = 0;
            foreach (NodeVector nvIn in pnvsIn)
            {
                nvIn.ConnectSubset(oNot.InputNodes, iBit, iBit + nvIn.Length - 1);
                iBit += nvIn.Length;
            }

            return nvOut;
        }

        public static NodeVector OR(params NodeVector[] pnvsIn)
        {
            NodeVector nvFirst = pnvsIn[0];
            return OR(nvFirst.ParentComponent, pnvsIn);
        }

        public static NodeVector OR(Component poComponent, params NodeVector[] pnvsIn)
        {
            string sFirstName = pnvsIn[0].Name;
            int iBits = 0;
            foreach (NodeVector nvIn in pnvsIn)
            {
                iBits += nvIn.Length;
            }

            string sName = poComponent.NewComponentName("or");
            OrGate oOr = new OrGate(poComponent, sName, iBits);
            sName = poComponent.NewNodeVectorName(oOr.Name + "_out");
            NodeVector nvOut = poComponent.CreateNodeVector(sName, iBits);
            nvOut.Connection = oOr.OutputNodes;

            int iBit = 0;
            foreach (NodeVector nvIn in pnvsIn)
            {
                nvIn.ConnectSubset(oOr.InputNodes, iBit, iBit + nvIn.Length - 1);
                iBit += nvIn.Length;
            }

            return nvOut;
        }


        public static NodeVector AND(params NodeVector[] pnvsIn)
        {
            NodeVector nvFirst = pnvsIn[0];
            return AND(nvFirst.ParentComponent, pnvsIn);
        }

        public static NodeVector AND(Component poComponent, params NodeVector[] pnvsIn)
        {
            string sFirstName = pnvsIn[0].Name;
            int iBits = 0;
            foreach (NodeVector nvIn in pnvsIn)
            {
                iBits += nvIn.Length;
            }

            string sName = poComponent.NewComponentName("and");
            AndGate oAnd = new AndGate(poComponent, sName, iBits);
            sName = poComponent.NewNodeVectorName(oAnd.Name + "_out");
            NodeVector nvOut = poComponent.CreateNodeVector(sName, iBits);
            nvOut.Connection = oAnd.OutputNodes;

            int iBit = 0;
            foreach (NodeVector nvIn in pnvsIn)
            {
                nvIn.ConnectSubset(oAnd.InputNodes, iBit, iBit + nvIn.Length - 1);
                iBit += nvIn.Length;
            }

            return nvOut;
        }

        public static NodeVector CONST(Component poComponent, int piValue)
        {
            string sName = poComponent.NewComponentName("const_" + piValue.ToString());
            Constant oConst = new Constant(poComponent, sName, piValue,Conversion.MinBitWidth(piValue));
            sName = poComponent.NewNodeVectorName(oConst.Name + "_out");
            NodeVector nvOut = poComponent.CreateNodeVector(sName, oConst.OutputNodes.Length);
            nvOut.Connection = oConst.OutputNodes;
            return nvOut;
        }

        public static NodeVector RESIZE(NodeVector pnvIn, int piWidth)
        {
            if (pnvIn.Length == piWidth)
                return pnvIn;

            if (pnvIn.Length < piWidth)
                return pnvIn.PadZero(pnvIn.ParentComponent, piWidth);

            string sName = pnvIn.ParentComponent.NewNodeVectorName(pnvIn.Name + "_trunc_" + piWidth.ToString());
            NodeVector nvOut = pnvIn.ParentComponent.CreateNodeVector(sName, piWidth);
            nvOut.ConnectSubset(pnvIn, 0, piWidth - 1);
            return nvOut;
        }

        // NEQ

        //GT

        //LT

        //GTEQ

        //LTEQ

        //
    }
}
